from os import error
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

def randomFrag(idxRan,M,NRan):
	'''
	随机生成索引段，返回一个列表，列表中的每一项都是一个包含两个元素的列表。
	:param idxRan: list,包含两个索引，是随机数生成的范围限定
	:param M: 表示要生成多少个列表项
	:param NRan: list,包含两个索引，表示一个索引段的长度的限定范围
	:return: list,如[[45,90],[1,65],[123,192],...]
	'''
	result=[]
	for i in range(M):
		N=np.random.randint(*NRan)
		loc=np.random.randint(idxRan[0],idxRan[1]-N)
		result.append([loc,loc+N])
	return result

def scaleOut(data,ran,fac):
	"""[如果data的最小值与下限的距离比最大值与上限的距离小，则向超越下限缩放]

	Args:
		data ([numpy array]): [一维数组]
		ran ([list]): [上下限范围]
		fac ([flot]): [缩放系数，如0.1表示超出上限或者下线的10%（相对于上下限范围）]
	
	Returns:
		[numpy array]: [缩放得到的数据]
	"""
	min=np.nanmin(data)
	max=np.nanmax(data)
	r=(ran[1]-ran[0])*fac
	if min-ran[0]<ran[1]-max:
		scale=((ran[0]-r)-max)/(min-max)
		return (data - max) * scale +max
	else:
		scale=((ran[1]+r)-min)/(max-min)
		return (data-min)*scale+min
	
def randomSeam(data,M,NRan,errorRan,warnRan,errorProb=0.3,warnProb=0.6,noise=False):
	"""[从data中随机取多段数据进行缝合，缝合时后一段数据会偏移到前一段数据的尾部]

	Args:
		data ([numpy array]): [一维数组，用于缝合的参考数据]
		M ([int]): [决定取多少段数据进行缝合]
		NRan ([list]): [列表包含两个整型数，表示每一段数据的随机长度范围]
		errorRan ([list]): [列表包含两个数，表示错误的范围]
		warnRan ([type]): [列表包含两个数，表示预警的范围]
		errorProb (float, optional): [模拟错误的概率]. Defaults to 0.2.
		warnProb (float, optional): [模拟预警的概率]. Defaults to 0.5.
		noise (bool, optional): [是否添加噪声]. Defaults to False.

	Returns:
		[numpy array]: [缝合得到的数据]
	"""
	errorDiff=errorRan[1]-errorRan[0]
	warnDiff=warnRan[1]-warnRan[0]
	idxRan=[0,data.shape[0]]
	idxFrags=randomFrag(idxRan,M,NRan)
	lastTail=data[idxFrags[0][0]:idxFrags[0][1]][-1]
	seamData=np.array([])
	for frag in idxFrags:
		sub=data[frag[0]:frag[1]].copy()
		head=sub[0]
		diff=head-lastTail
		sub-=diff
		max=np.nanmax(sub)
		min=np.nanmin(sub)
		if noise:
			r=max-min
			sub+=np.random.uniform(-0.05,0.05,size=sub.shape)*r
		seamData=np.append(seamData,sub)
		lastTail=sub[-1]
	seamData=(seamData-np.nanmin(seamData))/(np.nanmax(seamData)-np.nanmin(seamData))#归一化
	seamData=seamData*errorDiff+errorRan[0]#参照errorRan反归一化

	error8=errorDiff*0.8+errorRan[0]
	error2=errorDiff*0.2+errorRan[0]
	warn8=warnDiff*0.8+warnRan[0]
	warn2=warnDiff*0.2+warnRan[0]
	left=0
	e=errorDiff*0.05
	w=warnDiff*0.05
	for frag in idxFrags:
		right=left+frag[1]-frag[0]
		sub=seamData[left:right]
		min=np.nanmin(sub)
		max=np.nanmax(sub)
		diff=max-min
		rand=np.random.random()
		#除了满足概率条件，还需实现满足这一段的数据接近于错误限制且这一段的数据有明显起伏，避免中间段平缓突然被模拟为错误
		if rand<errorProb and diff>e and (min<error2 or max>error8):
			sub=scaleOut(sub,errorRan,np.random.uniform(0.05,0.2))
			seamData[left:right]=sub
		elif rand<warnProb and diff>w and (min<warn2 or max>warn8):
			sub=scaleOut(sub,warnRan,np.random.uniform(0.05,0.2))
			seamData[left:right]=sub
		else:
			pass
		left=right
	return seamData
	
if __name__=="__main__":
	# np.random.seed(3)
	filePath = '../data/一号泵.csv'
	data = pd.read_csv(filePath, encoding="utf-8")
	data = data['瞬时流量']
	data = data.values
	MAX=np.nanmax(data)
	MIN=np.nanmin(data)
	simData=randomSeam(data,500,[50,100],[MIN,MAX],[(MAX-MIN)*0.05+MIN,(MAX-MIN)*0.95+MIN])
	x = [i for i in range(1, len(simData) + 1)]
	plt.plot(x, simData)
	plt.axhline(MIN)
	plt.axhline(MAX)
	plt.show()
	
	